/**
 * @file task_led.c
 * @brief led management by layer(logic)
 * @author Tome (zigbee86@163.com)
 * @date 2021-01-05
 */
 

#include "OSAL.h" 
#include "osal_log.h"

#include "hal_led.h"
#include "hal_key.h"
#include "hal_host.h"

#include "stdio.h"

#include "task_led.h"
#include "task_time.h"


#include "onboard.h"

 
/****************************************************************************************************************************** 
                                                            const
*******************************************************************************************************************************/


//LOG_MOD_LED_ENABLE
#if LOG_CODE(LED)
const enum_v_str_t _CODE_LEDEVT_STR[] = 
{        
    ENUM_V_STR(LEDEVT_POWER),
    ENUM_V_STR(LEDEVT_SLP),
    
    ENUM_V_STR(LEDEVT_FSTART),
    ENUM_V_STR(LEDEVT_FOVER),
    ENUM_V_STR(LEDEVT_FABORT),
    
    ENUM_V_STR(LEDEVT_BSTART),
    ENUM_V_STR(LEDEVT_BOVER),
    ENUM_V_STR(LEDEVT_BABORT),
    ENUM_V_STR(LEDEVT_MAX),
    
};


#define  GET_LEDEVT_STR(e)            get_enum_string(e, LEDEVT_MAX, _CODE_LEDEVT_STR)

#else

#define  GET_LEDEVT_STR(...)

#endif



/****************************************************************************************************************************** 
                                                            typedef
*******************************************************************************************************************************/

//#define LED_NUM_LIST        4

typedef struct _led_ctrl
{
    //uint8 leds;
    uint8 sta;
    uint8 duty;
    uint16 period;
    uint8 cycle;
    
    uint8 shot;
}led_ctrl_t;


typedef struct led_layer_t_
{
    led_ctrl_t led[BOARD_LED_NUMBER];
    uint16     holdtime;        // how many time shoud stay in this layer
    uint16     gaptime;        // time bewteen two  led layer moving
    uint8      active;          // which led in this layer is active? if all led process is over , active = 0x00
}led_layer_t;



/*
    2   warn   (highest)
    1   once
    0   normal (lowest)
*/
#define LED_LAYER       3

typedef struct _led_list
{
    led_layer_t layer[LED_LAYER];
    uint8       launch;             // which layer is runing
    uint8       next;
}led_list_t;



#if BOARD_LED_ENABLE

/****************************************************************************************************************************** 
                                                            MICRO
*******************************************************************************************************************************/

#define malloc  osal_mem_alloc
#define free    osal_mem_free
#define memset  osal_memset


/****************************************************************************************************************************** 
                                                            declearation
*******************************************************************************************************************************/


static void task_led_ProcessOSALMsg(osal_event_hdr_t *pMsg );
void task_led_Handle_evt( uint8 leds, uint8 event );

static void task_led_brush_handler(void);
static void task_led_launch_next_layer(void);
static void task_led_layer_life_end(uint8 n);


/****************************************************************************************************************************** 
                                                            defination
*******************************************************************************************************************************/




uint8 Led_TaskID;


static led_list_t LED;


/**************************************************************************************************
 * @fn      Hal_Init
 *
 * @brief   Hal Initialization function.
 *
 * @param   task_id - Hal TaskId
 *
 * @return  None
 **************************************************************************************************/
void Led_Init( uint8 task_id )
{  
    
  UNUSED_VARIABLE(Led_TaskID);
    
  /* Register task ID */
  Led_TaskID = task_id;
    
  RegisterForLeds(task_id);
  memset(&LED, 0, sizeof(LED));
    
  //LEDLOG("led task %d start", task_id);
    
  //osal_pwrmgr_task_state( task_id, PWRMGR_HOLD);
    
   
    //HalLedBlink(HAL_LED_ALL, 10, 50, 1000);
    //HalLedBreath(HAL_LED_ALL, 3, 50, 1000);
    
  //task_led_set(LED_LAYER_NORMAL, HAL_LED_ALL, HAL_LED_MODE_FLASH, 99, 10000, 1 );
  //task_led_set(LED_LAYER_NORMAL, HAL_LED_ALL, HAL_LED_MODE_BREATH, 50, 2000, 255 );
    
    task_led_set(LED_LAYER_ONCE,  HAL_LED_ALL, HAL_LED_MODE_BREATH, 50, 1000, 3 );
    

}



/**************************************************************************************************
 * @fn      Led_ProcessEvent
 *
 * @brief   Test Process Event
 *
 * @param   task_id - Hal TaskId
 *          events - events
 *
 * @return  None
 **************************************************************************************************/
uint16 Led_ProcessEvent( uint8 task_id, uint16 events )
{
  uint8 *msgPtr;
  
  (void)task_id;  // Intentionally unreferenced parameter

  if ( events & SYS_EVENT_MSG )
  {
        msgPtr = osal_msg_receive(task_id);

        while (msgPtr)
        {
          /* Do something here - for now, just deallocate the msg and move on */
            
          //LEDLOG("T %02d, %s", task_id,msgPtr);
            
           task_led_ProcessOSALMsg((osal_event_hdr_t *)msgPtr );     

          /* De-allocate */
          osal_msg_deallocate( msgPtr );
          /* Next */
          msgPtr = osal_msg_receive( task_id );
        }
      return (events ^ SYS_EVENT_MSG );
  }
  
  if ( OSAL_EVENT_MATCH(TASK_LED_EVT_BRUSH_LAYER) )
  {
      task_led_brush_handler();
      OSAL_EVENT_KICK(TASK_LED_EVT_BRUSH_LAYER);
  }
  
  if ( OSAL_EVENT_MATCH(TASK_LED_EVT_NEXT_LAYER))
  {
      task_led_launch_next_layer();
      OSAL_EVENT_KICK(TASK_LED_EVT_NEXT_LAYER);
  }
  
  /*   need report data to led  */
  // if ( OSAL_EVENT_MATCH(TASK_HOST_EVT_REPORT) )
  // {
      // report_handler();
      // OSAL_EVENT_KICK( TASK_HOST_EVT_REPORT );
  // }

  

   return 0;
}




/******************************************************************************
* @fn        task_led_ProcessOSALMsg
*
* @brief     osal system events handler
*
* @param     
*
* @return    none
*/
static void task_led_ProcessOSALMsg(osal_event_hdr_t *pMsg )
{
    switch ( pMsg->event )
      {
//      case UART_EVENT:
//        //task_led_HandleUarts ( ((osalUartEvt_t*)pMsg)->event );
//        
//        break;

        
      // case KEY_CHANGE:
        // task_led_HandleKeys(  ((keyChange_t*)pMsg)->state, ((keyChange_t*)pMsg)->keys );
        // break;
      
      // case HOSTWK_EVENT:
          // task_led_HandleLedwk(  ((ledEvent_t*)pMsg)->event);
      
      // break;
    case HW_LED:
      task_led_Handle_evt( ((ledEvent_t*)pMsg)->leds, ((ledEvent_t*)pMsg)->event );
    break;
        
      default:
        break;
      }  
}




/*********************************************************************
 * @fn      task_led_Handle_evt
 *
 * @brief   Handles all led events for this device.
 *
 * @param   leds - which led
 * @param   event  - what happen
 *
 * @return  none
 */
void task_led_Handle_evt( uint8 leds, uint8 event )
{
    LEDLOG("LED:%X  %s", leds, GET_LEDEVT_STR(event ));
    
    switch(event)
    {
        case LEDEVT_SLP:
            
            osal_pwrmgr_task_state( Led_TaskID, PWRMGR_CONSERVE);
            //LEDLOG("LEDEVT_SLP");
            break;
        
        case LEDEVT_POWER:
            
            osal_pwrmgr_task_state( Led_TaskID, PWRMGR_HOLD );
            //LEDLOG("LEDEVT_POWER");
            break;
        
        case LEDEVT_FSTART:
            //LEDLOG("LEDEVT_FSTART : %d",leds);
            break;
        
        
        case LEDEVT_BSTART:
           // LEDLOG("LEDEVT_BSTART : %d",leds);
            break;
        
        case LEDEVT_FOVER:
        case LEDEVT_BOVER:
            //LEDLOG("LEDEVT_BOVER : %x",leds);
            task_led_layer_life_end(leds);
            break;
        
        default:
            break;
    
    }         
    
}

/*********************************************************************
 * @fn      task_led_brush_handler
 *
 * @brief   
 *
 * @param   
 *
 * @return  none
 */
static void task_led_brush_handler(void)
{
    uint8 i = 0;
    uint8 target = 0x01;
    uint16 holdtime = 0;
    
    uint8 layer = 0;
    led_ctrl_t *led;
    
    #if 0
    // find highest layer
    for ( layer = LED_LAYER_WARN; layer > 0 ; layer-- ) {if ( LED.layer[layer].active > 0 ) break; }
    
    if ( layer != LED.launch )
    {
        LEDLOG("brush layer %d->%d",LED.launch, layer);
        
        LED.launch = layer;
    }
    
    //led = LED.layer[layer].led;
    #else
   
    LED.launch = LED.next;
    layer = LED.launch;
    #endif
    

    led = LED.layer[layer].led;
    
    LEDLOG("brush layer %d ", layer );
    
    for ( i = 0; i < BOARD_LED_NUMBER; i++ )
    {
        if ( led[i].shot == 1 )
        { 
            //if ( led[i].sta == HAL_LED_MODE_OFF) HalLedSet(target, HAL_LED_MODE_OFF);
            goto __next;
        }
        
        if (( led[i].sta <= HAL_LED_MODE_ON )||  (led[i].sta == HAL_LED_MODE_TOGGLE ))
        {
            HalLedSet(target, led[i].sta);
            LED.layer[layer].active &= ~target;
            led[i].shot = 1;
        }
        else if ( led[i].sta == HAL_LED_MODE_FLASH )
        {
            HalLedBlink(target, led[i].cycle, led[i].duty, led[i].period);
            holdtime = 0xFFFF;
            led[i].shot = 1;
        }
        else if ( led[i].sta == HAL_LED_MODE_BREATH )
        {
            HalLedBreath(target, led[i].cycle, led[i].duty, led[i].period);
            holdtime = 0xFFFF;
            led[i].shot = 1;
        }
        
        
        __next:
        target <<= 1;
    }
    
    if ( LED.launch > 0 )
    {
        if ( holdtime != 0xFFFF )
        {
            osal_start_timerEx(Led_TaskID, TASK_LED_EVT_NEXT_LAYER, LED.layer[LED.launch].holdtime);
        }
        else
        {
            LED.layer[LED.launch].gaptime = 3000;
        }
    }
}


/*********************************************************************
 * @fn      task_led_launch_next_layer
 *
 * @brief   
 *
 * @param   
 *
 * @return  none
 */
static void task_led_layer_life_end(uint8 leds)
{
    uint8 i = 0;
    uint8 layer = LED.launch;
    led_ctrl_t *led = LED.layer[layer].led;
    
    uint8 bit = 0x01;
    
    LEDLOG("LED %x ", leds);
    
    for ( i = 0; i < BOARD_LED_NUMBER; i++ )
    {
        if ( leds & bit )
        {
            led[i].sta = HAL_LED_MODE_OFF;
            led[i].cycle = 0;
//            led[i].duty = 0;
            //led[i].shot = 1;
            HalLedSet(bit, HAL_LED_MODE_OFF);
            
            LED.layer[layer].active &= ~bit;
        }
        bit <<= 1;
    }
    
    if ( LED.layer[layer].active == 0)osal_set_event(Led_TaskID, TASK_LED_EVT_NEXT_LAYER);
}


/*********************************************************************
 * @fn      task_led_launch_next_layer
 *
 * @brief   
 *
 * @param   
 *
 * @return  none
 */
static void task_led_launch_next_layer(void)
{
    #define LAUNCH_NEXT_LATCH_TIME_MS       10
    
    uint8 i, layer;
    uint16 hold =  LED.layer[LED.launch].gaptime;   // gaptime ,ms
    led_ctrl_t *led;
//    
    for ( layer = LED_LAYER_WARN; layer > 0 ; layer-- ) {if ( LED.layer[layer].active > 0 ) break; }
    LEDLOG("led layer %d->%d",LED.launch, layer);
    
    
    if ( layer < LED.launch ) // move to low
    {
        
    }
    else
    {
        hold = LAUNCH_NEXT_LATCH_TIME_MS; // ms
        
        if ( layer > LED.launch )       // move to higher
        {
            // if brush layer is not NORMAL_LAYER, stop it
            LED.layer[LED.launch].active = 0x00;
            
            led = LED.layer[LED.launch].led;
            for ( i = 0; i < BOARD_LED_NUMBER; i++ ){led[i].shot = 1;} 
        }
        else if ( layer == LED.launch ) // same layer brush
        {
            
        }
    }
    
    // if fall to normal layer, force bursh 
    if ( layer == LED_LAYER_NORMAL )
    {
        led = LED.layer[layer].led;
        LED.layer[layer].active |= HAL_LED_ALL;
        for ( i = 0; i < BOARD_LED_NUMBER; i++ ){led[i].shot = 0;}
    }
    
    
    #if 0
    // we need keep the normal layer alwasy bursh 
    if ( layer == LED_LAYER_NORMAL )
    {
        led = LED.layer[layer].led;
        LED.layer[layer].active |= HAL_LED_ALL;
        for ( i = 0; i < BOARD_LED_NUMBER; i++ ){led[i].shot = 0;}
        
        hold = LAUNCH_NEXT_LATCH_TIME_MS; // ms
    }
    
    else if ( layer > LED.launch )
    {
        // if brush layer is not NORMAL_LAYER, stop it
        LED.layer[LED.launch].active = 0x00;
        
        led = LED.layer[LED.launch].led;
        for ( i = 0; i < BOARD_LED_NUMBER; i++ ){led[i].shot = 1;}
        
        hold = LAUNCH_NEXT_LATCH_TIME_MS; // ms
    }
    else if ( layer == LED.launch )
    {
        hold = LAUNCH_NEXT_LATCH_TIME_MS; // ms
    }
    else
    {}
    #endif
    
    //LED.launch = layer;
    
    LED.next = layer;
    

    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    osal_start_timerEx(Led_TaskID, TASK_LED_EVT_BRUSH_LAYER, hold );
}

/*********************************************************************
 * @fn      task_led_launch_next_layer
 *
 * @brief   
 *
 * @param   
 *
 * @return  none
 */
void task_led_set_lower(uint8 layer, uint8 leds, uint8 mode, uint8 duty, uint16 period, uint8 cycle, uint8_t reset)
{
    uint8 i = 0;
//    uint8 brush = 0;
    led_ctrl_t *led = LED.layer[layer].led;
    uint8 bit = 0x01;
    
    if ( layer > LED_LAYER_WARN ) return;
    
    // LEDLOG
    LOG("L %d,LED %x", layer, leds);
    
    leds &= HAL_LED_ALL;
    
    for ( i = 0; i < BOARD_LED_NUMBER; i++ )
    {
        if ( leds & bit )
        {
            led[i].sta = mode;
            led[i].duty = duty;
            led[i].period = period;
            led[i].cycle = cycle;
            led[i].shot = 0;
            //LED.layer[layer].active |= bit;
        }
        else
        {
            // keep the ohter led state
            if ( reset )
            {
                led[i].sta = HAL_LED_MODE_OFF;
                led[i].duty = 0;
                led[i].shot = 0;
                //LED.layer[layer].active &= ~bit;
            }
        }
        bit <<= 1;
    }
    LED.layer[layer].active |= leds;
    LED.layer[layer].holdtime = 0xFFFF;
    LED.layer[layer].gaptime = 3000; // ms
    
    if ( layer < LED.launch ) return ;
    
    
    // osal_stop_timerEx(Led_TaskID, TASK_LED_EVT_BRUSH_LAYER);
    // osal_clear_event(Led_TaskID,TASK_LED_EVT_BRUSH_LAYER);
    osal_set_event( Led_TaskID, TASK_LED_EVT_NEXT_LAYER );
    //osal_start_timerEx(Led_TaskID, TASK_LED_EVT_NEXT_LAYER, 10);
    

}

void task_led_clear( uint8 layer , uint8 leds)
{
    uint8 i = 0;
//    uint8 brush = 0;
    led_ctrl_t *led = LED.layer[layer].led;
    
    uint8 bit = 0x01;
//    uint8 tonext = 0;
//    
//    if ( LED.layer[layer].active & leds )
//    {
//        tonext = 1;
//    }

    for ( i = 0; i < BOARD_LED_NUMBER; i++ )
    {
        if ( leds & bit )
        {
            led[i].sta = HAL_LED_MODE_OFF;
            led[i].duty = 0;
            led[i].cycle = 0;
            led[i].shot = 0;
        }
        else
        {
            // keep the ohter led state
        }
        bit <<= 1;
    }
    LED.layer[layer].active &= ~leds;
    LED.layer[layer].holdtime = 0xFFFF;
    LED.layer[layer].gaptime = 3000; // ms
    
    // clear the the other layer
    if ( layer != LED.launch ) return ;
    
    /*
    if ( layer == LED.launch )
    {
        
    }
    */
    
     osal_stop_timerEx(Led_TaskID, TASK_LED_EVT_BRUSH_LAYER);
     osal_clear_event(Led_TaskID,TASK_LED_EVT_BRUSH_LAYER);
     //osal_set_event( Led_TaskID, TASK_LED_EVT_BRUSH_LAYER );
     osal_start_timerEx(Led_TaskID, TASK_LED_EVT_BRUSH_LAYER, 10);
    
}


void task_led_set(uint8 layer, uint8 leds, uint8 mode, uint8 duty, uint16 period, uint8 cycle)
{
    task_led_set_lower(layer, leds, mode, duty, period, cycle, FALSE );
}

void task_led_set_cover(uint8 layer, uint8 leds, uint8 mode, uint8 duty, uint16 period, uint8 cycle)
{
    task_led_set_lower(layer, leds, mode, duty, period, cycle, TRUE );
}


/*********************************************************************
 * @fn      task_led_launch_next_layer
 *
 * @brief   
 *
 * @param   
 *
 * @return  none
 */
void task_led_err_code(uint8 code)
{
    uint8 leds = HAL_LED_1;
//    uint8 mode = HAL_LED_MODE_FLASH;
    uint8 duty = 20;
    uint16 period = 600;
    uint8 cycle = 0, i = 0;
    uint8 cycles[3] = {0,0,0};
    
    
    LEDLOG("EC %d", code);
    
    /*
        1           *
        2           *   *
        3           *   *   *
    
        4           *2  *   *
        5           *2  *2  *
        6           *2  *2  *2
        6           *3  *2  *2
    ......
    */
    
    cycle = code / 3;
    for ( i = 0; i < 3; i++){cycles[i] = cycle;}
    cycle = code % 3;
    for ( i = 0; i < cycle; i++){cycles[i]++;}
    
    cycle = cycles[0];
    
    
    task_led_clear(LED_LAYER_WARN, HAL_LED_ALL);
    for ( i = 0; i < 3; i++)
    {
        if (cycles[i]) task_led_set(LED_LAYER_WARN, leds,  HAL_LED_MODE_FLASH,  duty,  period,  cycles[i]);
        leds <<= 1;
    }
    task_led_set(LED_LAYER_WARN, HAL_LED_4,  HAL_LED_MODE_ON,  0,  0,  0);
}





#else  //BOARD_LED_ENABLE



void Led_Init( uint8 task_id ){}

uint16 Led_ProcessEvent( uint8 task_id, uint16 events ){return 0;}

void task_led_set_lower(uint8 layer, uint8 leds, uint8 mode, uint8 duty, uint16 period, uint8 cycle, uint8_t reset){ }

void task_led_err_code(uint8 code){}

#endif // BOARD_LED_ENABLE



